/*
 *Copyright (c) [2019-2020]  C2comm, Inc.  All rights reserved.
 *
 *This program is free software; you can redistribute it and/or
 *modify it under the terms of the GNU General Public License
 *as published by the Free Software Foundation; either version 2
 *of the License, or (at your option) any later version.
 *
 *This program is distributed in the hope that it will be useful,
 *but WITHOUT ANY WARRANTY; without even the implied warranty of
 *MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *GNU General Public License for more details.
 *
 *You should have received a copy of the GNU General Public License
 *along with this program; If not, see <https://www.gnu.org/licenses/>
 */

/*
 *Vendor: C2comm
 *Version: 1.0
 *Filename: PCFArrayCtrl.v
 *Target Device: Altera
 *Author : 刘晓骏
*/


module PCFArrayCtrl(
    input  wire        SYS2CORE_clk,
    input  wire        SYS2CORE_rst_n,
    
    input  wire [47:0] G_device_clk,

    input  wire        PC2PAC_pcf_rx_cb_wr,
    input  wire [85:0] PC2PAC_pcf_rx_cb,
    output wire        PAC2PC_pcf_rx_cb_ready,

    output reg         PAC2CPS_pcf_rx_cb_wr,
    output reg  [85:0] PAC2CPS_pcf_rx_cb,
    input  wire        CPS2PAC_pcf_rx_cb_ready,
    
    output wire        PAC2PC_array_empty
);
/*//////////////////////////////////////////////////////////
                    中间变量声明区域
*///////////////////////////////////////////////////////////
//本模块中所有中间变量(wire/reg/parameter)在此集中声明 
//PermntArrayRam信号
wire        pcbuffer_wrreq;
wire [85:0] pcbuffer_wdata;
reg         pcbuffer_wrack;

reg         arrayram_rw;//0:read 1:write
reg  [5 :0] arrayram_addr;
reg  [91:0] arrayram_wdata;
wire [91:0] arrayram_rdata;

//地址回收信号
reg         alloc_ack;
wire        alloc_valid;
wire [5 :0] alloc_addr;  
reg         recycle_valid;
reg  [5 :0] recycle_addr;
//地址分配FIFO信号
reg         allocfifo_wrreq;
reg  [5:0]  allocfifo_wdata;
wire        allocfifo_rdreq;
                 
wire [5:0]  allocfifo_rdata;
wire [5:0]  allocfifo_usedw;
wire        allocfifo_empty;
wire        allocfifo_full;

//状态监控信号
reg  [ 5:0] array_head_ptr;

reg  [ 5:0] insert_sum;//插入队列时，当前已读取的节点数量
reg  [ 5:0] array_usedw;//队列已存储有效节点数量

reg  [91:0] array_head_data;//缓存队列头节点

reg  [91:0] modify_array_data;//缓存准备修改的队列数据，方便插入操作
reg  [ 5:0] modify_array_ptr; 

reg         read_insert_flag;//标记当前RAM读属于哪个流程 0:read  1:insert

reg  [ 5:0] array_state;//队列控制状态机

localparam  IDLE_S          = 6'b000001,
            READ_WAIT0_S    = 6'b000010,
            READ_WAIT1_S    = 6'b000100,
            MATCH_S         = 6'b001000,
            MODIFY_S        = 6'b010000,
            POP_HEAD_S      = 6'b100000;
            
reg  [1:0]  alloc_state;

localparam  INIT_S  = 2'b01,
            ALLOC_S = 2'b10;
/*//////////////////////////////////////////////////////////
             队列顺序控制功能(ArrayOrderCtrl)
*///////////////////////////////////////////////////////////
assign PAC2PC_array_empty = (array_usedw==6'b0) && (pcbuffer_wrreq==1'b0);

always @(posedge SYS2CORE_clk or negedge SYS2CORE_rst_n) begin
    if(SYS2CORE_rst_n == 1'b0) begin
        pcbuffer_wrack          <= 1'b0;
        PAC2CPS_pcf_rx_cb_wr    <= 1'b0;
        
        arrayram_rw             <= 1'b0;
        arrayram_addr           <= 6'b0;
        arrayram_wdata          <= 92'b0;
        
        alloc_ack               <= 1'b0;
        recycle_valid           <= 1'b0;
        recycle_addr            <= 6'b0;
        
        array_head_ptr          <= 6'b0;
        array_usedw             <= 6'b0;
        insert_sum              <= 6'b0;
        
        array_head_data         <= 92'b0;
        
        read_insert_flag        <= 1'b0;
        array_state             <= IDLE_S;
    end
    else begin
        case(array_state)
            IDLE_S: begin
                insert_sum <= 6'b0;
                if((pcbuffer_wrreq == 1'b1) && (alloc_valid == 1'b1))begin
                    pcbuffer_wrack       <= 1'b1;
                    recycle_valid        <= 1'b0;
                    PAC2CPS_pcf_rx_cb_wr <= 1'b0;
                 
                    if(array_usedw == 6'b0) begin
                        alloc_ack            <= 1'b1;
                        arrayram_rw          <= 1'b1;
                        array_usedw          <= array_usedw+1'b1;
                        arrayram_addr        <= alloc_addr;
                        arrayram_wdata       <= {pcbuffer_wdata,6'b0};
                     
                        array_head_ptr       <= alloc_addr;
                        array_head_data      <= {pcbuffer_wdata,6'b0};
                        
                        array_state          <= IDLE_S;
                    end
                    else begin
                        alloc_ack            <= 1'b0;
                        arrayram_rw          <= 1'b0;
                        arrayram_addr        <= array_head_ptr;
                        
                        arrayram_wdata[91:6] <= pcbuffer_wdata;
                        
                        read_insert_flag     <= 1'b1;

                        array_state          <= READ_WAIT0_S;
                    end
                    
                end
                else if((CPS2PAC_pcf_rx_cb_ready == 1'b1) && 
                        (array_usedw>6'd0) && 
                        (array_head_data[91:44]<=G_device_clk)) begin
                    pcbuffer_wrack           <= 1'b0;
                    alloc_ack                <= 1'b0;
                    if(array_usedw == 6'd1) begin
                        recycle_valid          <= 1'b1;
                        recycle_addr         <= array_head_ptr;
                        array_usedw          <= 6'b0;
                                             
                        PAC2CPS_pcf_rx_cb_wr <= 1'b1;
                        PAC2CPS_pcf_rx_cb    <= array_head_data[91:6];
                                             
                        arrayram_rw          <= 1'b1;
                        arrayram_wdata       <= 92'b0;
                                             
                        array_state          <= IDLE_S;
                    end
                    else begin
                        PAC2CPS_pcf_rx_cb_wr <= 1'b0;
                                             
                        recycle_valid          <= 1'b0;
                                             
                        arrayram_rw          <= 1'b0;
                        arrayram_addr        <= array_head_data[5:0];
                                             
                        read_insert_flag     <= 1'b0;
                                             
                        array_state          <= READ_WAIT0_S;
                    end 
                end
                else begin
                    pcbuffer_wrack       <= 1'b0;
                    PAC2CPS_pcf_rx_cb_wr <= 1'b0;
                                         
                    arrayram_rw          <= 1'b0;
                                         
                    alloc_ack            <= 1'b0;
                    recycle_valid        <= 1'b0;
                                         
                    array_state          <= IDLE_S;
                end
            end
            
            READ_WAIT0_S: begin
                pcbuffer_wrack       <= 1'b0;
                alloc_ack            <= 1'b0;
                insert_sum <= insert_sum + 1'b1;  
                array_state          <= READ_WAIT1_S;         
            end
            
            READ_WAIT1_S: begin
                if(read_insert_flag==1'b0) array_state <= POP_HEAD_S; 
                else                       array_state <= MATCH_S;         
            end
            
            MATCH_S: begin
                if(arrayram_wdata[91:44] > arrayram_rdata[91:44]) begin
            
                    if(insert_sum == array_usedw) begin
                        alloc_ack             <= 1'b1;
                        arrayram_rw           <= 1'b1;
                        arrayram_addr         <= alloc_addr;
                                           
                        modify_array_data     <= {arrayram_rdata[91:6],alloc_addr};
                        modify_array_ptr      <= arrayram_addr;
                        if(insert_sum == 6'b1) begin
                        //modify by lxj 20200425
                      
                            array_head_data[5:0]  <= alloc_addr;
                        end
                        else begin
                            array_head_data[5:0]  <= array_head_data[5:0];
                        end
                                           
                        array_state           <= MODIFY_S;  
                    end
                    else begin
                        alloc_ack          <= 1'b0;
                        arrayram_rw        <= 1'b0;
                        arrayram_addr      <= arrayram_rdata[5:0];
                        
                        modify_array_data  <= arrayram_rdata;

                        modify_array_ptr   <= arrayram_addr;
                        
                        array_state        <= READ_WAIT0_S;  
                    end
                end
                else begin

                    alloc_ack           <= 1'b1;
                    arrayram_rw         <= 1'b1;
                    arrayram_addr       <= alloc_addr;
                    arrayram_wdata[5:0] <= arrayram_addr;
                    
                    if(insert_sum == 6'd1) begin
          
                        array_head_ptr        <= alloc_addr;
                        array_head_data       <= {arrayram_wdata[91:6],arrayram_addr};
                        
                        modify_array_data     <= arrayram_rdata[91:0];
                        modify_array_ptr      <= arrayram_addr;
                    end
                    else begin
                 
                        array_head_data[5:0] <= (insert_sum == 6'd2) ? 
                                                 alloc_addr:array_head_data[5:0];
                        
                        modify_array_data  <= {modify_array_data[91:6],alloc_addr};
                      
                    end
                    array_state        <= MODIFY_S; 
                end
            end
            
            MODIFY_S: begin
                alloc_ack           <= 1'b0;
                
                array_usedw         <= array_usedw+1'b1;
                
                arrayram_rw         <= 1'b1;
                arrayram_addr       <= modify_array_ptr;
                arrayram_wdata      <= modify_array_data;
                
                array_state         <= IDLE_S; 
            end
            
            POP_HEAD_S: begin//弹出当前队列头，将第二节点作为队列头
                recycle_valid        <= 1'b1;
                recycle_addr         <= array_head_ptr;
                
                arrayram_rw          <= 1'b1;//清零头部节点的存储位置
                arrayram_addr        <= array_head_ptr;
                arrayram_wdata       <= 92'b0;
                
                PAC2CPS_pcf_rx_cb_wr <= 1'b1;
                PAC2CPS_pcf_rx_cb    <= array_head_data[91:6];
                                     
                array_head_ptr       <= arrayram_addr;
                array_head_data      <= arrayram_rdata[91:0];
                            
                array_usedw          <= array_usedw-1'b1;
                                     
                array_state          <= IDLE_S; 
            end
            
            default: begin
                pcbuffer_wrack          <= 1'b0;
                PAC2CPS_pcf_rx_cb_wr    <= 1'b0;
                
                arrayram_rw             <= 1'b0;
                arrayram_addr           <= 6'b0;
                arrayram_wdata          <= 92'b0;
                
                alloc_ack               <= 1'b0;
                recycle_valid           <= 1'b0;
                recycle_addr            <= 6'b0;
                
                array_head_ptr          <= 6'b0;
                array_usedw             <= 6'b0;
                insert_sum              <= 6'b0;
                
                array_head_data         <= 92'b0;
                
                read_insert_flag        <= 1'b0;
                array_state             <= IDLE_S;
            end
        endcase
    end
end


/*//////////////////////////////////////////////////////////
            缓存RAM空间管理功能(RamSpaceMgmt)
*///////////////////////////////////////////////////////////
assign allocfifo_rdreq = alloc_ack;
assign alloc_addr      = allocfifo_rdata;
assign alloc_valid     = (alloc_state == ALLOC_S) ? ~allocfifo_empty : 1'b0;

always @(posedge SYS2CORE_clk or negedge SYS2CORE_rst_n) begin
    if(SYS2CORE_rst_n == 1'b0) begin
        allocfifo_wrreq <= 1'b0;
        allocfifo_wdata <= 6'h0;
        alloc_state  <= INIT_S;
    end
    else begin
        case(alloc_state)
            INIT_S: begin
                allocfifo_wrreq <= 1'b1;
                allocfifo_wdata <= allocfifo_wdata + 1'b1;
                if(allocfifo_wdata==6'h3f) begin
                    alloc_state  <= ALLOC_S;
                end
                else begin
                    alloc_state  <= INIT_S;
                end
            end
            
            ALLOC_S: begin
                allocfifo_wrreq <= recycle_valid;
                allocfifo_wdata <= recycle_addr;
            end
            
            default: begin
                allocfifo_wrreq <= 1'b0;
                allocfifo_wdata <= 6'h0;
                alloc_state  <= INIT_S;
            end
        endcase
    end
end


/*//////////////////////////////////////////////////////////
                   IP调用区域
*///////////////////////////////////////////////////////////
//本模块调用的所有IP在该区域实例化
//例如fifo/ram/grant之类的IP.... 
AddrAllocFifo AddrAllocFifo(
    .clock (SYS2CORE_clk),
    .aclr  (~SYS2CORE_rst_n),
    .wrreq (allocfifo_wrreq),
    .data  (allocfifo_wdata),
    .rdreq (allocfifo_rdreq),
            
    .q     (allocfifo_rdata),
    .usedw (allocfifo_usedw),
    .empty (allocfifo_empty),
    .full  (allocfifo_full)
);
    
InportBuffer #(
    .DWIDTH(86)
)PcfCbBuffer(
    .clk(SYS2CORE_clk),
    .rst_n(SYS2CORE_rst_n),

    .idata_valid(PC2PAC_pcf_rx_cb_wr),
    .idata(PC2PAC_pcf_rx_cb),
    .idata_ready(PAC2PC_pcf_rx_cb_ready),

    .odata_wrreq(pcbuffer_wrreq),
    .odata(pcbuffer_wdata),
    .odata_wrack(pcbuffer_wrack)
); 

PermntArrayRam PermntArrayRam (
    .clock   (SYS2CORE_clk),
    .data    (arrayram_wdata),   
    .address (arrayram_addr),
    .wren    (arrayram_rw),//0:read 1:write  
  
    .q       (arrayram_rdata)       
);

endmodule
/*
PCFArrayCtrl PCFArrayCtrl_inst(
    .SYS2CORE_clk(),
    .SYS2CORE_rst_n(),

    .G_device_clk(),

    .PC2PAC_pcf_rx_cb_wr(),
    .PC2PAC_pcf_rx_cb(),
    .PAC2PC_pcf_rx_cb_ready(),

    .PAC2CPS_pcf_rx_cb_wr(),
    .PAC2CPS_pcf_rx_cb(),
    .CPS2PAC_pcf_rx_cb_ready(),
    
    .PAC2PC_array_empty()
);
*/